#ifndef SKYBOX_H
#define SKYBOX_H

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <vector>
#include <iostream>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

#include "shader_s.h"
#include "stb_image.h"


class Skybox {
private:
	GLuint textureID;
	GLuint skyVAO, skyVBO;
	Shader* shader;

	void loadCubeMap(std::vector<std::string> faces);

public:
	Skybox();
	~Skybox();

	void render(glm::mat4 view, glm::mat4 proj);
};

Skybox::Skybox() {
	float vertices[] = {
		// positions          
		-15.0f,  15.0f, -15.0f,
		-15.0f, -15.0f, -15.0f,
		 15.0f, -15.0f, -15.0f,
		 15.0f, -15.0f, -15.0f,
		 15.0f,  15.0f, -15.0f,
		-15.0f,  15.0f, -15.0f,

		-15.0f, -15.0f,  15.0f,
		-15.0f, -15.0f, -15.0f,
		-15.0f,  15.0f, -15.0f,
		-15.0f,  15.0f, -15.0f,
		-15.0f,  15.0f,  15.0f,
		-15.0f, -15.0f,  15.0f,

		 15.0f, -15.0f, -15.0f,
		 15.0f, -15.0f,  15.0f,
		 15.0f,  15.0f,  15.0f,
		 15.0f,  15.0f,  15.0f,
		 15.0f,  15.0f, -15.0f,
		 15.0f, -15.0f, -15.0f,

		-15.0f, -15.0f,  15.0f,
		-15.0f,  15.0f,  15.0f,
		 15.0f,  15.0f,  15.0f,
		 15.0f,  15.0f,  15.0f,
		 15.0f, -15.0f,  15.0f,
		-15.0f, -15.0f,  15.0f,

		-15.0f,  15.0f, -15.0f,
		 15.0f,  15.0f, -15.0f,
		 15.0f,  15.0f,  15.0f,
		 15.0f,  15.0f,  15.0f,
		-15.0f,  15.0f,  15.0f,
		-15.0f,  15.0f, -15.0f,

		-15.0f, -15.0f, -15.0f,
		-15.0f, -15.0f,  15.0f,
		 15.0f, -15.0f, -15.0f,
		 15.0f, -15.0f, -15.0f,
		-15.0f, -15.0f,  15.0f,
		 15.0f, -15.0f,  15.0f
	};

	glGenVertexArrays(1, &skyVAO);
	glGenBuffers(1, &skyVBO);

	glBindBuffer(GL_ARRAY_BUFFER, skyVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	
	glBindVertexArray(skyVAO);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	std::vector<std::string> faces;
	faces.reserve(6);
	faces.push_back("skybox_textures/skybox_px.jpg");
	faces.push_back("skybox_textures/skybox_nx.jpg");
	faces.push_back("skybox_textures/skybox_py.jpg");
	faces.push_back("skybox_textures/skybox_ny.jpg");
	faces.push_back("skybox_textures/skybox_nz.jpg");
	faces.push_back("skybox_textures/skybox_pz.jpg");
	loadCubeMap(faces);
	
	shader = new Shader(std::string("shaders/skybox.vert").c_str(), std::string("shaders/skybox.frag").c_str());

	shader->use();
	shader->setInt("skybox", 0);
}

Skybox::~Skybox() {
	glDeleteVertexArrays(1, &skyVAO);
	glDeleteBuffers(1, &skyVBO);
	delete shader;
}

void Skybox::render(glm::mat4 view, glm::mat4 proj) {
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
	
	shader->use();
	shader->setMat4("view", view);
	shader->setMat4("projection", proj);

	glBindVertexArray(skyVAO);
	glDrawArrays(GL_TRIANGLES, 0, 36);
}

void Skybox::loadCubeMap(std::vector<std::string> faces) {
	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
	
	int width, height, nrChannels;
	unsigned char* data;

	
	for (GLuint i = 0; i < faces.size(); i++) {
		stbi_set_flip_vertically_on_load(true);
		data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
		GLenum format;
		if (nrChannels == 3) format = GL_RGB;
		else if (nrChannels == 4) format = GL_RGBA;
		if (data) {
			glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		}
		else {
			std::cout << "Cube texture failed to load at path: " << faces[i] << std::endl;
		}
		stbi_image_free(data);
	}
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

#endif
